package org.axgl
{

	
	
public class AxEntity extends AxRect
{
	public function AxEntity(x:Number = 0, y:Number = 0) 
	{
		super(x, y);

		visible = true;
		active = true;
		solid = true;
		exists = true;
		
		center = new AxPoint(x + width / 2, y + height / 2);
		previous = new AxPoint(x, y);
		velocity = new AxVector;
		pvelocity = new AxVector;
		acceleration = new AxVector;
		maxVelocity = new AxVector(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
		angle = 0;
		drag = new AxVector;
		offset = new AxRect;
		phased = false;
		stationary = false;
		worldBounds = null;
	}

	
	
	public static const LEFT:uint = 1;
	public static const RIGHT:uint = 2;
	public static const UP:uint = 4;
	public static const DOWN:uint = 8;
	public static const NONE:uint = 0;
	public static const ANY:uint = LEFT | RIGHT | UP | DOWN;

	
	

	public function update():void
	{
		touched = touching;
		touching = NONE;
		
		previous.x = x;
		previous.y = y;
		pvelocity.x = velocity.x;
		pvelocity.y = velocity.y;
		
		if (stationary || (velocity.x == 0 && velocity.y == 0 && velocity.a == 0 && acceleration.x == 0 && acceleration.y == 0 && acceleration.a == 0)) {
			return;
		}
		
		velocity.x = calculateVelocity(velocity.x, acceleration.x, drag.x, maxVelocity.x);
		velocity.y = calculateVelocity(velocity.y, acceleration.y, drag.y, maxVelocity.y);
		velocity.a = calculateVelocity(velocity.a, acceleration.a, drag.a, maxVelocity.a);
		
		x += (velocity.x * Ax.dt) + ((pvelocity.x - velocity.x) * Ax.dt / 2);
		y += (velocity.y * Ax.dt) + ((pvelocity.y - velocity.y) * Ax.dt / 2);
		angle += velocity.a * Ax.dt;
		
		center.x = x + width / 2;
		center.y = y + height / 2;
		
		if (worldBounds != null) {
			if (x < worldBounds.x) {
				velocity.x = 0;
				acceleration.x = Math.max(0, acceleration.x);
				x = worldBounds.x;
			} else if (x + width > worldBounds.width) {
				velocity.x = 0;
				acceleration.x = Math.min(0, acceleration.x);
				x = worldBounds.width - width;
			}
			
			if (y < worldBounds.y) {
				velocity.y = 0;
				acceleration.y = Math.max(0, acceleration.y);
				y = worldBounds.y;
			} else if (y + height > worldBounds.height) {
				velocity.y = 0;
				acceleration.y = Math.min(0, acceleration.y);
				y = worldBounds.height - height;
			}
		}
	}


	public function draw():void 
	{
		// override as needed
	}
	
	/**
	 * Calculates the velocity for a single axis using the current velocity, acceleration, drag, and terminal velocity.
	 * 
	 * @param velocity The current velocity of this axis.
	 * @param acceleration The current acceleration of this axis.
	 * @param drag The current drag of this axis.
	 * @param terminal The current terminal velocity of this axis.
	 *
	 * @return The new velocity based on the inputs and the timestep.
	 */
	private function calculateVelocity(velocity:Number, acceleration:Number, drag:Number, terminal:Number):Number
	{
		if (acceleration != 0) {
			velocity += acceleration * Ax.dt;
		} else {
			var dragEffect:Number = drag * Ax.dt;
			if (velocity - dragEffect > 0) {
				velocity -= dragEffect;
			} else if (velocity + dragEffect < 0) {
				velocity += dragEffect;
			} else {
				velocity = 0;
			}
		}
		
		if (velocity > terminal) {
			velocity = terminal;
		} else if (velocity < -terminal) {
			velocity = -terminal;
		}
		
		return velocity;
	}

	
	public function destroy():void 
	{
		exists = false;
	}

	
	/**
	 * Returns whether or not this object is touching a solid object in the direction(s) passed. You can test
	 * a single direction (<code>isTouching(LEFT)</code>) or multiple at once (<code>isTouching(LEFT | DOWN)</code>).
	 *
	 * @param directions The direction flag(s) to test.
	 *
	 * @return True if this object is touching any of the directions passed.
	 *
	 * @see #wasTouching()
	 */
	public function isTouching(directions:uint):Boolean {
		return (touching & directions) > NONE;
	}

	/**
	 * Returns whether or not this object was touching a solid object in the direction(s) passed during the previous
	 * frame.
	 *
	 * @param directions The direction flag(s) to test.
	 *
	 * @return True if this object was touching any of the directions passed.
	 *
	 * @see #isTouching()
	 */
	public function wasTouching(directions:uint):Boolean {
		return (touched & directions) > NONE;
	}
	
	/**
	 * @inheritDoc
	 */
	override public function overlaps(other:AxRect):Boolean
	{
		if (!exists || (other is AxEntity && !(other as AxEntity).exists)) 
		{
			return false;
		}
		return super.overlaps(other);
	}
	
	
	public function dispose():void 
	{
		velocity = null;
		pvelocity = null;
		acceleration = null;
		maxVelocity = null;
		previous = null;
		previous = null;
		offset = null;
		drag = null;
		worldBounds = null;
	}
	
	
	
	public var visible:Boolean;  // 是否可见（渲染）
	public var active:Boolean;  // 是否更新
	public var solid:Boolean;  // 是否为固体（碰撞）
	public var exists:Boolean;  // 是否存在（调用destroy，设为false）
	public var velocity:AxVector;
	public var acceleration:AxVector;
	public var angle:Number;
	public var maxVelocity:AxVector;
	public var drag:AxVector;  // 摩擦力
	/**
	 * The offset of the bounding box of this entity.
	 * <p>If an entity is loaded with an image that is 100x100, you can use <code>offset, width, and height</code> to
	 * change the bounding box that will affect collisions. The width and height determine the size of the bounding box,
	 * and offset determines how far to the right and down the upper left corner of the bounding box is.</p>
	 */
	public var offset:AxPoint;
	/**
	 * Read-only. Contains the position that this entity had during the previous frame.
	 */
	public var previous:AxPoint;
	/**
	 * A phased entity will trigger collisions, but will act as if it is not solid. Set this to true if you must use
	 * <code>Ax.collide</code> instead of overlap, but you don't want the entities to be affected by colliding.
	 *
	 * @default false
	 */
	public var phased:Boolean;
	/**
	 * Contains which sides this entity is touching on, or'd together. For example, if an entity is standing on the floor
	 * and is moving against a wall to his right, the value of this entity will be DOWN | RIGHT. You can find if an entity
	 * is touching a side using either <code>if (touching &amp; LEFT)</code> or <code>if (isTouching(LEFT))</code>.
	 *
	 * @default NONE
	 */
	public var touching:uint;
	/**
	 * Contains the sides this object was touching the previous frame. See <code>touching</code> for more details.
	 *
	 * @default NONE
	 */
	public var touched:uint;
	/**
	 * For entities that should not move, set this to true in order to skip the calculations for movement. This can be a performance
	 * gain if you have a lot of entities that do not move.
	 *
	 * @default false
	 */
	public var stationary:Boolean;
	/**
	 * Read-only. Contains the midpoint of this entity, updated every frame.
	 */
	public var center:AxPoint;
	/**
	 * Read-only. Contains the velocity that this entity had during the previous frame.
	 */
	public var pvelocity:AxVector;
	/**
	 * The bounds limiting where this entity can move. If null, there are no bounds.
	 */
	public var worldBounds:AxRect;
	/** Counter that allow you to disable counting this function's update for the debugger */
	public var countUpdate:Boolean = true;
	/** Counter that allow you to disable counting this function's draw for the debugger */
	public var countDraw:Boolean = true;

}
}
